﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Authentication;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using K9Nano.Application;
using K9Nano.Application.Extensions;
using K9Nano.Authentication;
using K9Nano.Authorization;
using K9Nano.Domain;
using K9Nano.Runtime;
using K9Nano.Share;
using K9Nano.Share.Exceptions;
using K9Nano.Web.Accounts.Dto;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using ISystemClock = K9Nano.Runtime.ISystemClock;

namespace K9Nano.Web.Accounts
{
    public class AccountAppService<TUser> : K9ApplicationService, IAccountAppService where TUser : User, new()
    {
        protected readonly UserManager<TUser> UserManager;
        protected readonly ISystemClock SystemClock;
        protected readonly IUserBlacklistChecker BlacklistChecker;
        protected readonly IUserBackListManager UserBackListManager;

        public AccountAppService(IUnitOfWork uow,
            IMapper mapper,
            ISessionContext sessionContext,
            ILoggerFactory loggerFactory,
            UserManager<TUser> userManager,
            ISystemClock systemClock,
            IUserBlacklistChecker blacklistChecker,
            IUserBackListManager userBackListManager) : base(uow, mapper, sessionContext, loggerFactory)
        {
            UserManager = userManager;
            SystemClock = systemClock;
            BlacklistChecker = blacklistChecker;
            UserBackListManager = userBackListManager;
        }

        #region 用户

        /// <summary>
        /// 创建用户
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns>新用户的ID</returns>
        public virtual async ValueTask<Guid> CreateAsync(UserCreateInput input, CancellationToken cancellation)
        {
            if (await BlacklistChecker.IsForbiddenAsync(input.Phone, input.ClientId, cancellation))
            {
                throw new K9UserFriendlyException("此手机号在黑名单中");
            }

            var repository = Uow.Repository<TUser, Guid>();
            if (await repository.AnyAsync(x => x.UserName == input.Phone, cancellation))
            {
                throw new K9UserFriendlyException("此手机号已被注册");
            }

            var user = MapToUser(input);

            return await Uow.RunTransactionAsync(async () =>
            {
                IdentityResult result = null;
                if (string.IsNullOrEmpty(input.Password))
                {
                    result = await UserManager.CreateAsync(user);
                }
                else
                {
                    result = await UserManager.CreateAsync(user, input.Password);
                }

                result.EnsureSuccess();

                var claims = new List<Claim>(6)
                {
                    new Claim(K9SharedConstants.TenantIdClaimType, SessionContext.TenantId?.ToString() ?? string.Empty), // TenantId
                    new Claim(K9SharedConstants.NameClaimType, input.Name), // 姓名
                    new Claim(K9SharedConstants.PhoneNumberClaimType, input.Phone), // 手机号
                    new Claim(K9SharedConstants.GenderClaimType, input.Gender.GetGender()) // 性别
                };
                // Email
                if (!string.IsNullOrEmpty(input.Email))
                {
                    claims.Add(new Claim(K9SharedConstants.EmailClaimType, input.Email));
                }

                if (input.Roles == null || input.Roles.Length == 0)
                {
                    // 根据ClientId 设置新用户的默认角色
                    var roleRepository = Uow.Repository<Role, Guid>();
                    var defaultRoles = await roleRepository.GetAllListAsync(x => x.Default, cancellation);
                    input.Roles = defaultRoles.Select(x => x.Name).ToArray();
                }

                // 角色
                if (input.Roles.Length > 0)
                {
                    result = await UserManager.AddToRolesAsync(user, input.Roles);
                    result.EnsureSuccess();
                    var roleClaim = new Claim(K9SharedConstants.RoleClaimType, string.Join(",", input.Roles));
                    claims.Add(roleClaim);
                }
                // 头像
                if (!string.IsNullOrEmpty(input.Avatar))
                {
                    claims.Add(new Claim(K9SharedConstants.ProfileClaimType, input.Avatar));
                }

                if (claims.Count > 0)
                {
                    result = await UserManager.AddClaimsAsync(user, claims);
                    result.EnsureSuccess();
                }
                return user.Id;
            });
        }

        protected virtual TUser MapToUser(UserCreateInput input)
        {
            var user = new TUser
            {
                UserName = input.Phone,
                PhoneNumber = input.Phone,
                PhoneNumberConfirmed = true,
                Email = input.Email
            };
            return user;
        }
        /// <summary>
        /// 更新用户基本信息
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask UpdateBaseInfoAsync(UserEditInput input, CancellationToken cancellation)
        {
            var user = await UserManager.FindByIdAsync(input.Id);
            if (user == null) throw new K9UserFriendlyException("用户不存在");
            var claims = await UserManager.GetClaimsAsync(user);

            await Uow.RunTransactionAsync(async () =>
            {
                if (!string.IsNullOrEmpty(input.Name))
                {
                    var nameClaim = claims.FirstOrDefault(x => x.Type == K9SharedConstants.NameClaimType);
                    if (nameClaim == null)
                    {
                        (await UserManager.AddClaimAsync(user, new Claim(K9SharedConstants.NameClaimType, input.Name))).EnsureSuccess();
                    }
                    else
                    {
                        (await UserManager.ReplaceClaimAsync(user, nameClaim, new Claim(K9SharedConstants.NameClaimType, input.Name))).EnsureSuccess();
                    }
                }

                var genderClaim = claims.FirstOrDefault(x => x.Type == K9SharedConstants.GenderClaimType);
                if (genderClaim == null)
                {
                    (await UserManager.AddClaimAsync(user, new Claim(K9SharedConstants.GenderClaimType, input.Gender.GetGender()))).EnsureSuccess();
                }
                else
                {
                    (await UserManager.ReplaceClaimAsync(user, genderClaim,
                        new Claim(K9SharedConstants.GenderClaimType, input.Gender.GetGender()))).EnsureSuccess();
                }

                if (!string.IsNullOrEmpty(input.Avatar))
                {
                    var avatarClaim = claims.FirstOrDefault(x => x.Type == K9SharedConstants.ProfileClaimType);
                    if (avatarClaim == null)
                    {
                        (await UserManager.AddClaimAsync(user, new Claim(K9SharedConstants.ProfileClaimType, input.Avatar))).EnsureSuccess();
                    }
                    else
                    {
                        (await UserManager.ReplaceClaimAsync(user, avatarClaim,
                            new Claim(K9SharedConstants.ProfileClaimType, input.Avatar))).EnsureSuccess();
                    }
                }
            });

        }

        #endregion

        #region 授权

        /// <summary>
        /// 获取用角色列表
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask<IList<string>> GetRolesAsync(string id, CancellationToken cancellation)
        {
            var user = await UserManager.FindByIdAsync(id);
            if (user == null) throw new K9UserFriendlyException("用户不存在");
            var roles = await UserManager.GetRolesAsync(user);
            return roles;
        }

        /// <summary>
        /// 添加角色
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask AddRolesAsync(UserRoleEditInput input, CancellationToken cancellation)
        {
            var user = await UserManager.FindByIdAsync(input.Id);
            if (user == null) throw new K9UserFriendlyException("用户不存在");
            await Uow.RunTransactionAsync(async () =>
            {
                (await UserManager.AddToRolesAsync(user, input.Roles)).EnsureSuccess();
                var roles = await UserManager.GetRolesAsync(user);
                var roleClaim = new Claim(K9SharedConstants.RoleClaimType, string.Join(",", roles));
                (await UserManager.AddClaimAsync(user, roleClaim)).EnsureSuccess();
            });
        }

        /// <summary>
        /// 移除角色
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask RemoveRolesAsync(UserRoleEditInput input, CancellationToken cancellation)
        {
            var user = await UserManager.FindByIdAsync(input.Id);
            if (user == null) throw new K9UserFriendlyException("用户不存在");
            await Uow.RunTransactionAsync(async () =>
            {
                (await UserManager.RemoveFromRolesAsync(user, input.Roles)).EnsureSuccess();
                var roles = await UserManager.GetRolesAsync(user);
                var roleClaim = new Claim(K9SharedConstants.RoleClaimType, string.Join(",", roles));
                (await UserManager.AddClaimAsync(user, roleClaim)).EnsureSuccess();
            });
        }

        /// <summary>
        /// 禁止用户登录客户端
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask ForbidAsync(UserWithClientsInput input, CancellationToken cancellation)
        {
            await UserBackListManager.ForbidAsync(input.Id, input.Clients, input.Reason, cancellation);
        }

        /// <summary>
        /// 允许用户登录客户端
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public virtual async ValueTask AllowAsync(UserWithClientsInput input, CancellationToken cancellation)
        {
            await UserBackListManager.AllowAsync(input.Id, input.Clients, cancellation);
        }

        #endregion

        #region 获取用户信息

        public virtual async ValueTask<UserInfoDto> GetUserInfo()
        {
            if (SessionContext.UserId == null) return new UserInfoDto();

            var user = await UserManager.FindByIdAsync(SessionContext.UserId.Value.ToString());

            if (user == null) throw new AuthenticationException();

            var claims = await UserManager.GetClaimsAsync(user);
            // todo Cache ?
            return new UserInfoDto
            {
                Id = SessionContext.UserId.ToString(),
                Name = claims.FirstOrDefault(x => x.Type == K9SharedConstants.NameClaimType)?.Value,
                Avatar = claims.FirstOrDefault(x => x.Type == K9SharedConstants.ProfileClaimType)?.Value,
                Email = claims.FirstOrDefault(x => x.Type == K9SharedConstants.EmailClaimType)?.Value,
            };
        }

        #endregion
    }
}